/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Armin Lunkeit
 *
 * This implementation is part of math-core
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
//
//  fraction.h
//  math-core
//
//  Created by Armin Lunkeit on 15.02.14.
//  Copyright (c) 2014 Armin Lunkeit. All rights reserved.
//

#ifndef math_core_fraction_h
#define math_core_fraction_h

/**
 * This class contains the represenation of a Fraction containing 
 * two 64 bit values. The representation is split into nominator and
 * denominator. Altough there might be many fraction classes or even
 * better implementations this one is used for the optimization problems
 * handled by this library.
 */

#include <stdint.h>
#include <sstream>
#include <memory>



class Fraction
{
public:
    /**
     * Empty constructor creating a Fraction looking like this: 0/1
     */
    Fraction();
    
    /**
     * Creates a fraction representation n / d
     */
    Fraction( int64_t n, int64_t d );
    
    /**
     * Parses a given string and creates the fraction representation.
     * The string must have the format "n/d" without whitespaces etc.
     */
    Fraction( const char* number );
    
    /**
     * Processes a given double value and creates a fraction represenation
     * from it.
     */
    Fraction( long double d );
    
    /**
     * Class destructor
     */
    ~Fraction();
    
    /**
     * Copy constructor
     */
    Fraction( Fraction const& orig )
    {
        _nominator = orig._nominator;
        _denominator = orig._denominator;
    }
    
    /**
     * Assignment
     */
    inline Fraction& operator = ( Fraction const& rhs )
    {
        _nominator = rhs._nominator;
        _denominator = rhs._denominator;
        return *this;
    }
    
    inline Fraction& operator = ( long double const& rhs )
    {
        initFromDouble( rhs );
        return *this;
    }
    
    inline Fraction& operator = ( double const& rhs )
    {
        initFromDouble( rhs );
        return *this;
    }
    
    inline Fraction& operator = ( int32_t const& rhs )
    {
        _nominator = rhs;
        _denominator = 1;
        return *this;
    }
    
    inline Fraction& operator = ( int64_t const& rhs )
    {
        _nominator = rhs;
        _denominator = 1;
        return *this;
    }
    
    
    /**
     * Compare for equality
     */
    inline bool operator == ( Fraction const& rhs )
    {
        normalize();
        Fraction t = rhs;
        t.normalize();
        
        return _nominator == t._nominator && _denominator == t._denominator;
    }
    
    /**
     * greater than
     */
    inline bool operator > ( Fraction const& rhs )
    {
        uint64_t t1 = _nominator * rhs._denominator;
        uint64_t t2 = _denominator * rhs._nominator;
        
        return t1 - t2 > 0;
    }
    
    /**
     * less than
     */
    inline bool operator < ( Fraction const& rhs )
    {
        int64_t t1 = _nominator * rhs._denominator;
        int64_t t2 = _denominator * rhs._nominator;
        
        return t1 - t2 < 0;
    }
    
    /**
     * Adds two Fraction values and returns the result
     */
    Fraction operator+( Fraction const& rhs  );
    
    Fraction operator+( uint32_t const& v );
        
    Fraction operator += (Fraction const& rhs );
        
    Fraction operator += (uint32_t const& rhs );
    
    /**
     * Substracts one fraction from the other and returns the result
     */
    Fraction operator-( Fraction const& rhs );
        
    Fraction operator-( uint32_t const& v );
        
    Fraction operator -= (Fraction const& v );
        
    Fraction operator -= (uint32_t const& v );
    
    /**
     * divides two fractions
     */
    Fraction operator / (Fraction const& rhs );
    
    Fraction operator / (uint32_t const& v );
    Fraction operator /= (Fraction const& v );
    Fraction operator /= (uint32_t const& v);
        
    /**
     * multiplies two fractions
     */
    Fraction operator * (Fraction const & rhs );
    
    /**
     * Multiply fraction with scalar value
     */
    Fraction operator * (uint32_t const v );
       
    /**
     * combined multiply and assignment operator
     */
    Fraction operator *= (uint32_t const v);
    
    /**
     * combined multiply and assignment operator
     */
    Fraction operator *= (Fraction const & rhs );
    
    
    inline uint64_t nominator() const
    {   return _nominator; }
    
    inline uint64_t denominator() const
    {   return _denominator; }
    
    inline bool isFraction() const
    { return _denominator != 1; }
    
    /**
     * Implicit type conversion operator to long double. Allows something
     * like long double v = Fraction( 2, 5 );
     */
    operator long double ()
    { return (long double)_nominator / (long double)_denominator; }

protected:
    
    /**
     * nominator of the fraction
     */
    int64_t _nominator;
    
    /**
     * denominator of the fraction
     */
    int64_t _denominator;
    
    /**
     * Computes the greatest common divisor
     */
    int64_t gcd( int64_t a, int64_t b );
    
    /**
     * Normalizes the internal representation
     */
    void normalize();
        
    void initFromDouble( long double x );
    
    /**
     * Friend operator used to print the Fraction to the command line
     */
    friend std::ostream& operator<<(std::ostream& lhs, Fraction const& rhs);
        
    
};

typedef std::shared_ptr<Fraction> PFraction;

std::ostream& operator<<(std::ostream& lhs, Fraction const& rhs);

#endif
